package com.biyao.controller.config.aop;


import com.biyao.schedulingcommon.response.BaseMsg;
import com.biyao.schedulingcommon.response.FailureMsg;
import com.biyao.schedulingcommon.response.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Global exception controller.
 * <p>
 * Handles all exceptions, if the exception have a response status,
 * set the HTTP status to that, else set the HTTP status to 500.
 * <p>
 * Error message displays only if the response status presents,
 * having the first non-empty value of following:
 * <ol>
 * <li>Response status reason</li>
 * <li>Localized message of exception</li>
 * <li>"Unknown error"</li>
 * </ol>
 * <p>
 * This handler also log every exception the generate 5XX error.
 *
 *
 */
@RestControllerAdvice
@Component
public class GlobalExceptionController {

    /**
     * 日志记录类.
     */
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionController.class);


    /**
     * 异常处理类.
     *
     * @param e Exception
     * @return ErrorResult
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public final FailureMsg handleException(HttpServletRequest request, Exception e) {
        e.printStackTrace();
        logger.error(e.getMessage());
        int code = StatusCode.SERVER_ERROR.getKey();
        ResponseStatus status = e.getClass().getAnnotation(ResponseStatus.class);
        if (status != null) {
            code = status.value().value();
        }

        //todo 调试程序过程中这个可以注释掉 大于500的状态码 统一为500
//        if (code >= StatusCode.SERVER_ERROR.getKey()) {
//            return new FailureMsg(StatusCode.SERVER_ERROR);
//        }
        return new FailureMsg(code, e.getMessage());
    }

    /**
     * 处理validator约束错误
     * @param ex
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public FailureMsg validationErrorHandler(ConstraintViolationException ex) {
        List<String> errorInformation = ex.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        return new FailureMsg(StatusCode.BAD_PARAM.getKey(),
                CollectionUtils.isEmpty(errorInformation) ? StatusCode.BAD_PARAM.getMsg() : errorInformation.get(0));
    }

    /**
     * 处理validator约束错误
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseMsg validationErrorHandler(MethodArgumentNotValidException ex) {
        // 同样是获取BindingResult对象，然后获取其中的错误信息
        // 如果前面开启了fail_fast，事实上这里只会有一个信息
        //如果没有，则可能又多个
        List<String> errorInformation = ex.getBindingResult().getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.toList());
        return new FailureMsg(StatusCode.BAD_PARAM.getKey(),
                CollectionUtils.isEmpty(errorInformation) ? StatusCode.BAD_PARAM.getMsg() : errorInformation.get(0));
    }
}
